﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml.Linq;
using AzukiMap.MapSource;
using AzukiMap.Renderer;
using AzukiMap.Tools.Logger;
using GeoAPI.Geometries;

namespace AzukiMap.Layer
{
    public delegate void VectorElementSelectedHandler(VectorLayer layer, int id, FrameworkElement element, bool isMulti);

    public abstract class VectorLayer : MapLayer, IVectorLayer
    {
        #region Events

        public event MouseButtonEventHandler ElementMouseLeftButtonDown;
        public event MouseButtonEventHandler ElementMouseLeftButtonUp;
        public event MouseEventHandler ElementMouseEnter;
        public event MouseEventHandler ElementMouseLeave;
        public event VectorElementSelectedHandler ElementSelected;
        public event VectorElementSelectedHandler ElementUnselected;
        public event VectorElementSelectedHandler ElementHighlighted;
        public event VectorElementSelectedHandler ElementUnhighlighted;

        #endregion

        #region Fields

        private Point _clickMouseDownPosition;
        protected IVectorStyle _highlightedStyle;
        protected VectorRendererSL _renderer;
        private Dictionary<int, FrameworkElement> _selectedElements;
        private IList<int> _selectedId;
        protected Dictionary<int, bool> _rendered;

#if !SILVERLIGHT
        bool _usesDrawingVisual = false;
        bool _usesRenderTargetBitmap = false;
#endif

        #endregion

        #region Constructor

        protected VectorLayer()
        {
            Init();
        }

        protected VectorLayer(IMapSource vectorData, string name)
            : base(vectorData.Type, vectorData, name)
        {
            MaxExtent = null;
            Init();
        }

        protected VectorLayer(IMapSource vectorData, string name, IPolygon maxExtent)
            : base(vectorData.Type, vectorData, name)
        {
            MaxExtent = maxExtent;
            Init();
        }

        private void Init()
        {
            IsRendering = false;
            LayerThemes = new List<VectorLayerTheme>();
            Labels = new List<string>();
            SetRenderer();
            DefaultStyle = new VectorStyle();

            LabelHorizontalAlignment = System.Windows.HorizontalAlignment.Center;
            LabelVerticalAlignment = VerticalAlignment.Center;
            ///When the scale changed recall selected events.
            LabelMargin = new Thickness(0);

            IsSelectable = false;
            IsMultiSelect = false;

            _selectedElements = new Dictionary<int, FrameworkElement>();
            _selectedId = new List<int>();
            _rendered = new Dictionary<int, bool>();
#if SILVERLIGHT
            this.CacheMode = new BitmapCache() {RenderAtScale = 2};
#endif
        }

        private void SetRenderer()
        {
#if !SILVERLIGHT
            if (_usesDrawingVisual)
                _renderer = new VectorRendererDrawingVisual(this);
            else if (_usesRenderTargetBitmap)
                _renderer = new VectorRendererTargetBitmap(this);
            else
                _renderer = new VectorRendererSL(this);
#else
            _renderer = new VectorRendererSL(this);
#endif
        }

        #endregion

        #region Properties

        public List<string> Labels { get; set; }
        public HorizontalAlignment LabelHorizontalAlignment { get; set; }
        public VerticalAlignment LabelVerticalAlignment { get; set; }
        public Thickness LabelMargin { get; set; }
        public bool IsSelectable { get; set; }
        private bool _isMultiSelect;

        public bool IsMultiSelect
        {
            get { return _isMultiSelect; }
            set
            {
                _isMultiSelect = value;
            }
        }

        public IEnumerable<int> SelectedElementIds
        {
            get
            {
                return _selectedId;
            }
        }

        public bool IsControlPressed
        {
            get
            {
                return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
            }
        }
        #endregion

        #region IVectorLayer Members

        public VectorSource VectorSource
        {
            get { return (VectorSource) base.MapSource; }
            protected set
            {
                if (value != null)
                    base.MapSource = value;
            }
        }

        public VectorSourceTypes VectorType { get; set; }

        public int ThemeCount
        {
            get { return LayerThemes.Count; }
        }

        public List<string> Expressions
        {
            get
            {
                var expressions = new List<string>();
                foreach (VectorLayerTheme theme in LayerThemes)
                {
                    expressions.Add(theme.Expression);
                }
                return expressions;
            }
        }

        public List<IVectorStyle> Styles
        {
            get
            {
                var styles = new List<IVectorStyle>();
                foreach (VectorLayerTheme theme in LayerThemes)
                {
                    styles.Add(theme.Style);
                }
                return styles;
            }
        }

        /// <summary>
        /// Style when a element highlighted.
        /// </summary>
        public IVectorStyle HighlightedStyle
        {
            get; set;
        }

        /// <summary>
        /// Style when a element selected.
        /// </summary>
        public IVectorStyle SelectedStyle
        {
            get; set;
        }

        public override void Show()
        {
            base.Show();
            Draw();
        }

        public IVectorStyle DefaultStyle { get; set; }

        public List<VectorLayerTheme> LayerThemes { get; set; }

        public Dictionary<int, FrameworkElement> SelectedElements
        {
            get { return _selectedElements; }
        }


#if SILVERLIGHT
        public override void Clear()
        {
            base.Clear();
            _selectedElements.Clear();
            //_selectedId.Clear();
            _rendered.Clear();
        }
#endif

#if !SILVERLIGHT
        public bool UsesDrawingVisual
        {
            get
            {
                return _usesDrawingVisual;
            }
            set
            {
                _usesDrawingVisual = value;
                SetRenderer();
            }
        }

        public bool UsesRenderTargetBitmap
        {
            get
            {
                return _usesRenderTargetBitmap;
            }
            set
            {
                _usesRenderTargetBitmap = value;
                SetRenderer();
            }
        }

        public override void Clear()
        {
            _rendered.Clear();
            if (_usesDrawingVisual || _usesRenderTargetBitmap)
            {
                ((VectorRendererDrawingVisual)_renderer).DrawingContainer.Children.Clear();
            }
            else 
            {
                base.Clear();
            }
        }
#endif

        public override XElement ToXml()
        {
            return ToXml("VectorLayer");
        }

        public override void Parse(XElement layerXml)
        {
            base.Parse(layerXml);
            XElement defaultStyleXml = layerXml.Element("DefaultStyle");
            DefaultStyle = new VectorStyle();
            DefaultStyle.Parse(defaultStyleXml);
            ParseThemes(layerXml.Element("LayerThemes"));
        }

        #endregion

        #region Theme control

        public int AddTheme(string expression, IVectorStyle style)
        {
            var theme = new VectorLayerTheme(expression, style);
            return AddTheme(theme);
        }

        public int AddTheme(VectorLayerTheme theme)
        {
            int themeOrder = -1;
            LayerThemes.Add(theme);
            themeOrder = LayerThemes.IndexOf(theme);
            return themeOrder;
        }

        public void RemoveTheme(int themeOrder)
        {
            try
            {
                LayerThemes.RemoveAt(themeOrder);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Logger.Error("Theme order number is out of range on RemoveLayerTheme().\n" + ex.StackTrace);
            }
        }

        public void RemoveAllThemes()
        {
            LayerThemes.Clear();
        }

        public void SetExpression(int themeOrder, string expression)
        {
            try
            {
                LayerThemes[themeOrder].Expression = expression;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Logger.Error("Theme order number is out of range on SetThemeExpression().\n" + ex.StackTrace);
            }
        }

        public string GetExpression(int themeOrder)
        {
            try
            {
                return LayerThemes[themeOrder].Expression;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Logger.Error("Theme order number is out of range on GetThemeExpression().\n" + ex.StackTrace);
                return null;
            }
        }

        public void SetStyle(int themeOrder, IVectorStyle style)
        {
            try
            {
                LayerThemes[themeOrder].Style = style;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Logger.Error("Theme order number is out of range on SetThemeStyle().\n" + ex.StackTrace);
            }
        }

        public IVectorStyle GetStyle(int themeOrder)
        {
            try
            {
                return LayerThemes[themeOrder].Style;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Logger.Error("Theme order number is out of range on GetThemeStyle().\n" + ex.StackTrace);
                return null;
            }
        }

        public override void MapScaleChanged(object sender, Map.MapScaleChangedArgs e)
        {
        }

        #endregion

        #region Events on elements

        protected void SetEventsOnElement(List<FrameworkElement> elems)
        {
            if (ElementMouseEnter != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseEnter += ElementMouseEnter;
                }
            }

            if (ElementMouseLeave != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseLeave += ElementMouseLeave;
                }
            }

            if (ElementMouseLeftButtonDown != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseLeftButtonDown += ElementMouseLeftButtonDown;
                }
            }

            if (ElementMouseLeftButtonUp != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseLeftButtonUp += ElementMouseLeftButtonUp;
                }
            }

            if (IsSelectable)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseEnter += ElemMouseEnter;
                    elem.MouseLeave += ElemMouseLeave;
                    elem.MouseLeftButtonDown += ElemMouseLeftButtonDown;
                    elem.MouseLeftButtonUp += ElemMouseLeftButtonUp;
                    elem.Cursor = Cursors.Hand;
                    //elem.Cursor = Cursors.Wait;
                }
            }

            if (IsSelectable != true &&
                ElementMouseEnter == null &&
                ElementMouseLeave == null &&
                ElementMouseLeftButtonDown == null &&
                ElementMouseLeftButtonUp == null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.IsHitTestVisible = false;
                }
            }

            
        }

        private void ElemMouseEnter(object sender, MouseEventArgs e)
        {
            var element = (FrameworkElement) sender;
            int id = GetElementId(element);
            if ( ! IsSelected(id)) {
                SetAsHighlightedByMouse(id, element);
            }
        }

        private void ElemMouseLeave(object sender, MouseEventArgs e)
        {
            var element = (FrameworkElement) sender;
            int id = GetElementId(element);
            if (!IsSelected(GetElementId(element)))
            {
                SetAsUnhighlightedByMouse(id, element);
            }
        }

        private void ElemMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _clickMouseDownPosition = e.GetPosition((UIElement) sender);
        }

        private void ElemMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var element = (FrameworkElement) sender;
            Point clickMouseUpPosition = e.GetPosition(element);
            if (clickMouseUpPosition.Equals(_clickMouseDownPosition))
            {
                int id = GetElementId(element);
                if (IsSelected(id))
                {
                    SetAsUnselected(id, element);
                }
                else
                {
                    SetAsSelectedByMouse(id, element);
                }
            }
        }

        void SetHighlightedStyle(FrameworkElement element)
        {
            var shapeElement = element as Shape;
            if (shapeElement != null)
                VectorRendererSL.SetVectorStyles(shapeElement, HighlightedStyle);
        }

        void SetNormalStyle(FrameworkElement element)
        {
            var shapeElement = element as Shape;
            if ( shapeElement != null )
                if (element.Resources.Contains("defaultStyle"))
                    VectorRendererSL.SetVectorStyles(shapeElement, (IVectorStyle) element.Resources["defaultStyle"]);
        }

        void SetSelectedStyle(FrameworkElement element)
        {
            var shapeElement = element as Shape;
            if (shapeElement != null)
                VectorRendererSL.SetVectorStyles(shapeElement, SelectedStyle);
        }

        protected void SetAsHighlighted(int id, FrameworkElement element)
        {
            SetHighlightStatusOnElement(element, true);
            SetHighlightedStyle(element);
        }

        protected void SetAsUnhighlighted(int id, FrameworkElement element)
        {
            SetHighlightStatusOnElement(element, false);
            SetNormalStyle(element);
        }

        protected void SetAsSelected(int id, FrameworkElement element)
        {
            SetSelectedStatusOnElement(element, true);
            SelectedElements.Add(id, element);
            if (! _selectedId.Contains(id))
                _selectedId.Add(id);
            SetSelectedStyle(element);
        }

        void SetAsHighlightedByMouse(int id, FrameworkElement element)
        {
            SetAsHighlighted(id, element);
            InvokeElementHighlighted(id, element);
        }

        private void SetAsUnhighlightedByMouse(int id, FrameworkElement element)
        {
            SetAsUnhighlighted(id, element);
            InvokeElementUnhighlighted(id, element);
        }

        private void SetAsSelectedByMouse(int id, FrameworkElement element)
        {
            if (! IsMultiSelect || ! IsControlPressed)
            {
                ClearSelectedElements();
            }
            SetAsSelected(id, element);
            InvokeElementSelected(id, element);
        }

        public void ClearSelectedElements()
        {
            foreach (var p in SelectedElements)
            {
                int id = p.Key;
                FrameworkElement lastSelectedElement = p.Value;
                if (lastSelectedElement != null)
                {
                    SetSelectedStatusOnElement(lastSelectedElement, false);
                    SetHighlightStatusOnElement(lastSelectedElement, false);
                    SetNormalStyle(lastSelectedElement);
                    InvokeElementUnselected(id, lastSelectedElement);
                }
            }
            SelectedElements.Clear();
            _selectedId.Clear();
        }

        private void SetAsUnselected(int id, FrameworkElement element)
        {
            element.Resources.Remove("IsSelected");
            SelectedElements.Remove(id);
            _selectedId.Remove(id);
            SetSelectedStatusOnElement(element, false);
            SetNormalStyle(element);
            InvokeElementUnselected(id, element);
        }

        void SetHighlightStatusOnElement( FrameworkElement element, bool isHighlighted)
        {
            const string resourceKey = "IsHighlighted";
            if ( isHighlighted)
                AddElementStatusKey(element, resourceKey);
            else
                RemoveElementStatusKey(element, resourceKey);
        }

        void SetSelectedStatusOnElement( FrameworkElement element, bool isSelected)
        {
            const string resourceKey = "IsSelected";
            if ( isSelected)
                AddElementStatusKey(element, resourceKey);
            else
                RemoveElementStatusKey(element, resourceKey);
        }

        protected bool IsSelectedStatus( FrameworkElement element )
        {
            return element.Resources.Contains("IsSelected");
        }

        void AddElementStatusKey( FrameworkElement element, string key )
        {
            if ( ! element.Resources.Contains(key))
            {
                element.Resources.Add(key, true);
            }
        }

        void RemoveElementStatusKey(FrameworkElement element, string key)
        {
            if ( element.Resources.Contains(key))
            {
                element.Resources.Remove(key);
            }
        }

        private void InvokeElementSelected(int id, FrameworkElement element)
        {
            VectorElementSelectedHandler selected = ElementSelected;
            if (selected != null) selected(this, id, element, (IsMultiSelect && IsControlPressed));
        }

        private void InvokeElementHighlighted(int id, FrameworkElement element)
        {
            VectorElementSelectedHandler highlighted = ElementHighlighted;
            if (highlighted != null) highlighted(this, id, element, (IsMultiSelect && IsControlPressed));
        }

        private void InvokeElementUnselected(int id, FrameworkElement element)
        {
            VectorElementSelectedHandler unselected = ElementUnselected;
            if (unselected != null) unselected(this, id, element, (IsMultiSelect && IsControlPressed));
        }

        private void InvokeElementUnhighlighted(int id, FrameworkElement element)
        {
            VectorElementSelectedHandler unhighlighted = ElementUnhighlighted;
            if (unhighlighted != null) unhighlighted(this, id, element, (IsMultiSelect && IsControlPressed));
        }

        private bool IsSelected(int id)
        {
            return SelectedElements.ContainsKey(id);
        }

        private static int GetElementId(FrameworkElement elem)
        {
            return (int) elem.Resources["id"];
        }

        #endregion


        #region XML parser

        public override XElement ToXml(string elementName)
        {
            XElement layerXml = base.ToXml(elementName);
            layerXml.Add(
                //new XElement("DefaultStyle", _defaultStyle.ToXml()),
                DefaultStyle.ToXml("DefaultStyle"),
                LayerThemesToXml(LayerThemes)
                );
            return layerXml;
        }

        public XElement LayerThemesToXml(List<VectorLayerTheme> themes)
        {
            var themesXml = new XElement("LayerThemes",
                                         from theme in themes
                                         //select new XElement("LayerTheme", theme.ToXml())
                                         select theme.ToXml() // VectorLayerTheme
                );
            return themesXml;
        }

        public void ParseThemes(XElement layerThemesXml)
        {
            foreach (XElement themeElem in layerThemesXml.Elements("VectorLayerTheme"))
            {
                var theme = new VectorLayerTheme();
                theme.Parse(themeElem);
                AddTheme(theme);
            }
        }

        #endregion
    }
}